/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkRtspMessage.h"
#include "MkRtspUtil.h"
#include "MkUtil/MkUtil.h"
#include "MkUtil/MkLog.h"

CMkRtspMessage::CMkRtspMessage()
{

}

const MkString& CMkRtspMessage::GetHeader(const MkRtspHeaderType& HeaderType) const
{
    MkMap<MkRtspHeaderType, MkString>::const_iterator it = m_MapHeader.find(HeaderType);
    if (it != m_MapHeader.end()) {
        return it->second;
    }
    return CMkUtil::GetEmptyString();
}

Uint32 CMkRtspMessage::AddHeader(const MkRtspHeaderType& HeaderType, const MkString& strValue)
{
    MkString strHeaderType = CMkRtspUtil::GetInstance().GetRtspHeaderType(HeaderType);
    if (strHeaderType.empty()) {
        return InvalidParams;
    }
    m_MapHeader[HeaderType] = strValue;
    return NoneError;
}

Uint32 CMkRtspMessage::AddBody(const MkString& Body)
{
    m_Body = Body;
    if (!m_Body.empty()) {
        AddHeader(MkRtspHeaderContentLength, std::to_string(Body.size()));
    }
    return NoneError;
}

const MkString& CMkRtspMessage::GetExtensionHeader(const MkString& Key) const
{
    MkMapString::const_iterator it = m_MapExtension.find(Key);
    if (it != m_MapExtension.end()) {
        return it->second;
    }
    return CMkUtil::GetEmptyString();
}

Uint32 CMkRtspMessage::AddExtensionHeader(const MkString& Key, const MkString& Value)
{
    m_MapExtension[Key] = Value;
    return NoneError;
}

Uint32 CMkRtspMessage::Parse(const Int8* Content, Uint32 nLen, Uint32& nUsed)
{
    MkString strLine;
    //first readheader
    Uint32 ErrorCode = CMkUtil::ReadLine(Content, nLen, nUsed, strLine);
    MkCheckErrorLog(ErrorCode, "read line failed content:%s\n", Content);

    ErrorCode = OnParseHeaderLine(strLine);
    MkCheckErrorLog(ErrorCode, "parse header line failed header:%s\n", strLine.c_str());

    MkString strKey;
    MkString strValue;
    while (1) {
        ErrorCode = CMkUtil::ReadLine(Content, nLen, nUsed, strLine);
        MkCheckErrorLog(ErrorCode, "read line failed  content:%s\n", Content);
        if (strLine.empty()) {
            break;
        }
        ErrorCode = ParseRtspHeader(strLine, strKey, strValue);
        if (NoneError != ErrorCode) {
            MkWarningLog("parse head:%s failed\n", strLine.c_str());
        } else {
            if (NoneError != CheckHeader(strKey, strValue)) {
                m_MapExtension.insert(std::pair<MkString, MkString>(strKey, strValue));
            }
        }
    }

    Uint32 ContentLen = GetContentLength();
    if (nLen >= ContentLen + nUsed) {
        m_Body = MkString(Content + nUsed, ContentLen);
        nUsed += ContentLen;
    } else {
        return NotHasEnoughBuffer;
    }
    return NoneError;
}

Uint32 CMkRtspMessage::GetCSeq() const
{
    MkMap<MkRtspHeaderType, MkString>::const_iterator it = m_MapHeader.find(MkRtspHeaderCSeq);
    if (it != m_MapHeader.end()) {
        return std::stoi(it->second);
    }
    return 0;
}

Uint32 CMkRtspMessage::GetContentLength()
{
    //find int entity header
    MkMap<MkRtspHeaderType, MkString>::iterator it = m_MapHeader.find(MkRtspHeaderContentLength);
    if (it != m_MapHeader.end()) {
        return std::stoi(it->second);
    }
    return 0;
}

Uint32 CMkRtspMessage::ParseRtspHeader(const MkString& strLine, MkString& strKey, MkString& strValue)
{
    MkVector<MkString> VecString;
    Uint32 ErrorCode = CMkUtil::StringSplit(strLine, ": ", VecString);
    MkCheckErrorLog(ErrorCode, "split string:%s failed\n", strLine.c_str());
    if (VecString.size() < 2) {
        return InvalidParams;
    }
    strKey = VecString[0];
    strValue = VecString[1];
    return NoneError;
}

Uint32 CMkRtspMessage::CheckHeader(const MkString& Key, const MkString& Value)
{
    MkRtspHeaderType EntrityType = CMkRtspUtil::GetInstance().GetMkHeaderType(Key);
    if (MkRtspHeaderUnknown == EntrityType) {
        return InvalidParams;
    }
    MkMap<MkRtspHeaderType, MkString>::iterator it = m_MapHeader.find(EntrityType);
    if (it == m_MapHeader.end()) {
        m_MapHeader[EntrityType] = Value;
    }
    return NoneError;
}

Uint32 CMkRtspMessage::ToString(MkString& strRtsp)
{
    Uint32 ErrorCode = OnPacketHeaderLine(strRtsp);
    MkCheckErrorLog(ErrorCode, "packet header failed\n");
    strRtsp += MkEndLine;
    PacketHeader(strRtsp);
    MkMapString::iterator it = m_MapExtension.begin();
    for (; it != m_MapExtension.end(); it++) {
        strRtsp += it->first + ": " + it->second + MkEndLine;
    }
    strRtsp += MkEndLine;
    if (!m_Body.empty()) {
        strRtsp += m_Body;
    }
    return NoneError;
}

Uint32 CMkRtspMessage::ParseAuthenticate(OUT BOOL& bBasic, OUT MkString& Realm, OUT MkString& Nonce) const
{
    MkString Authenticate = GetHeader(MkRtspHeaderWWWAuthenticate);
    if (Authenticate.empty()) {
        return InvalidParams;
    }
    MkString Author;
    size_t nPos = Authenticate.find("Basic ");
    //begin with basic
    if (MkString::npos != nPos) {
        bBasic = TRUE;
        Author = Authenticate.substr(nPos + strlen("Basic "));
    } else {
        nPos = Authenticate.find("Digest ");
        if (MkString::npos == nPos) {
            return InvalidParams;
        }
        bBasic = FALSE;
        Author = Authenticate.substr(nPos + strlen("Digest "));
    }

    MkVector<MkString> VesAuthor;
    Uint32 ErrorCode = CMkUtil::StringSplit(Author, ", ", VesAuthor);
    MkCheckErrorLog(ErrorCode, "spilt string failed\n");
    for (size_t i = 0; i < VesAuthor.size(); i++) {
        MkVector<MkString> vecTmp;
        ErrorCode = CMkUtil::StringSplit(VesAuthor[i], "=", vecTmp);
        if (NoneError != ErrorCode || vecTmp.size() < 2) {
            continue;
        }
        if ("realm" == vecTmp[0]) {
            Realm = vecTmp[1].substr(1, vecTmp[1].size() - 2);
        } else if ("nonce" == vecTmp[0]) {
            Nonce = vecTmp[1].substr(1, vecTmp[1].size() - 2);
        }
    }
    //if (Realm.empty() || Nonce.empty()) {
    //    return InvalidParams;
    //}
    return NoneError;
}

Uint32 CMkRtspMessage::ParseAuthorization(OUT MkString& UserName, OUT MkString& Realm, OUT MkString& Nonce,
    OUT MkString& Uri, OUT MkString& Response) const
{
    MkString Authorization = GetHeader(MkRtspHeaderAuthorization);
    if (Authorization.empty()) {
        return InvalidParams;
    }
    size_t nPos = Authorization.find("Digest ");
    if (MkString::npos == nPos) {
        return InvalidParams;
    }
    MkString Author = Authorization.substr(nPos + strlen("Digest "));
    MkVector<MkString> VesAuthor;
    Uint32 ErrorCode = CMkUtil::StringSplit(Author, ", ", VesAuthor);
    MkCheckErrorLog(ErrorCode, "spilt string failed\n");
    for (size_t i = 0; i < VesAuthor.size(); i++) {
        MkVector<MkString> vecTmp;
        ErrorCode = CMkUtil::StringSplit(VesAuthor[i], "=", vecTmp);
        if (NoneError != ErrorCode || vecTmp.size() < 2) {
            continue;
        }
        if ("realm" == vecTmp[0]) {
            Realm = vecTmp[1].substr(1, vecTmp[1].size() - 2);
        } else if ("nonce" == vecTmp[0]) {
            Nonce = vecTmp[1].substr(1, vecTmp[1].size() - 2);
        } else if ("username" == vecTmp[0]) {
            UserName = vecTmp[1].substr(1, vecTmp[1].size() - 2);
        } else if ("response" == vecTmp[0]) {
            Response = vecTmp[1].substr(1, vecTmp[1].size() - 2);
        } else if ("uri" == vecTmp[0]) {
            Uri = vecTmp[1].substr(1, vecTmp[1].size() - 2);
        }
    }
    if (Realm.empty()
        || Nonce.empty()
        || UserName.empty()
        || Response.empty()
        || Uri.empty()) {
        return InvalidParams;
    }
    return NoneError;
}

Uint32 CMkRtspMessage::PacketHeader(MkString& Headers)
{
    MkString StrKey;
    MkMap<MkRtspHeaderType, MkString>::iterator it = m_MapHeader.begin();
    for (; it != m_MapHeader.end(); it++) {
        StrKey = CMkRtspUtil::GetInstance().GetRtspHeaderType(it->first);
        if (!StrKey.empty()) {
            Headers += StrKey + ": " + it->second + MkEndLine;
        }
    }
    return NoneError;
}

CMkRtspMessage::~CMkRtspMessage()
{

}